//
//  ZBPaintingView.m
//  ZBOpenGLESDemo
//
//  Created by apple on 2018/7/11.
//  Copyright © 2018年 apple. All rights reserved.
//

#import "ZBPaintingView.h"
#import <GLKit/GLKit.h>
#import "ZBFileUntil.h"
#include "ZBShaderUntil.h"

#define kBrushOpacity        (1.0 / 3.0)
#define kBrushPixelStep        3
#define kBrushScale            2

// Texture 纹理
typedef struct {
    GLuint id;
    GLsizei width, height;
} ZBTextureInfoStruct;

//Shaders 着色器
enum {
    PROGRAM_POINT,
    NUM_PROGRAMS
};
enum {
    UNIFORM_MVP,
    UNIFORM_POINT_SIZE,
    UNIFORM_VERTEX_COLOR,
    UNIFORM_TEXTURE,
    NUM_UNIFORMS
};

enum {
    ATTRIB_VERTEX,
    NUM_ATTRIBS
};

typedef struct {
    char *vert, *frag;
    GLint uniform[NUM_UNIFORMS];
    GLuint id;
} programInfo_t;




programInfo_t program[NUM_PROGRAMS] = {
    { "point.vsh",   "point.fsh" },     // PROGRAM_POINT
};



@interface  ZBPaintingView()
{
    GLuint viewRenderbuffer, viewFramebuffer;
    Boolean needsErase;
    
    // 画布的大小
    GLint backingWidth;
    GLint backingHeight;
    
    // 缓存对象
    GLuint vboId;
    
    //纹理对象
    ZBTextureInfoStruct brushTexture;
    GLfloat brushColor[4];          // brush color
    
    //着色器对象
    GLuint vertexShader;
    GLuint fragmentShader;
    GLuint shaderProgram;
    
    //是否初始化
    BOOL initialized;
    
}

@property (nonatomic, strong)  EAGLContext *context;

/**
 *  保存当前画的一笔的所有点  画完保存后既删除
 */
@property (nonatomic, strong) NSMutableArray *pointsArrM;

// 当前点
@property(nonatomic, assign) CGPoint location;

// 上一个点
@property(nonatomic, assign) CGPoint previousLocation;

//占位文字
@property (nonatomic, strong) UILabel *placeHolderLabel;

@end


@implementation ZBPaintingView

+ (Class)layerClass
{
    return [CAEAGLLayer class];
}
- (instancetype)initWithFrame:(CGRect)frame
{
    if(self = [super initWithFrame:frame])
    {
        // 1、在init的方法中，从基类获取layer属性，并将其转型至CAEAGLLayer
        CAEAGLLayer *eaglLayer = (CAEAGLLayer *)super.layer;
        
        //2.无需Quartz处理透明度
        eaglLayer.opaque = YES;
        
        //3.设置属性
        eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                        [NSNumber numberWithBool:YES], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
        /*
         后面的参数有两个选择
         kEAGLRenderingAPIOpenGLES1=1  表示用渲染库的API版本是1.1
         kEAGLRenderingAPIOpenGLES2=2  表示用渲染库的API版本是2.0
         */
        self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
        
        // //设定当期上下文对象
        if (!self.context || ![EAGLContext setCurrentContext:self.context]) {
            return nil;
        }
        
        // Set the view's scale factor as you wish
        self.contentScaleFactor = [[UIScreen mainScreen] scale];
        
        needsErase = YES;
        
        //增加占位文字
        [self addSubview:self.placeHolderLabel];
        
    }
    return self;
}

//如果我们的视图的大小,我们将要求布局子视图。
//这是一个完美的机会来更新framebuffer这样
//同样大小的显示区域
- (void)layoutSubviews
{
     [EAGLContext setCurrentContext:self.context];
    
    if(!initialized){
        initialized = [self setupInitGL];
    }else{
        [self resizeFromLayer:(CAEAGLLayer*)self.layer];
    }
    
    if(needsErase){
        [self clearBoard];
        needsErase = NO;
    }
    
    self.placeHolderLabel.center = self.center;
    self.placeHolderLabel.bounds = self.bounds;
    
}

#pragma mark - API
- (void)setLineColor:(UIColor *)lineColor
{
    _lineColor = lineColor;
    NSArray *rgba = [self getRGBWithColor:lineColor];
    
    brushColor[0] = [rgba[0] floatValue];
    brushColor[1] = [rgba[1] floatValue];
    brushColor[2] = [rgba[2] floatValue];
    brushColor[3] = [rgba[3] floatValue];
    
    if (initialized) {
        glUseProgram(program[PROGRAM_POINT].id);
        // 设置画笔颜色
        glUniform4fv(program[PROGRAM_POINT].uniform[UNIFORM_VERTEX_COLOR], 1, brushColor);
    }
}

- (void)setLineWidth:(CGFloat)lineWidth
{
    _lineWidth = lineWidth;
    if(initialized){
         glUseProgram(program[PROGRAM_POINT].id);
        //设置画笔的宽度
        glUniform1f(program[PROGRAM_POINT].uniform[UNIFORM_POINT_SIZE], brushTexture.width / lineWidth);
    }
}

- (void)clearBoard
{
    [EAGLContext setCurrentContext:self.context];
    
    // Clear the buffer
    glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer);
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);
    
    // Display the buffer
    glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
    [self.context presentRenderbuffer:GL_RENDERBUFFER];
    
    self.placeHolderLabel.hidden = NO;
}


#pragma mark - 事件
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    
    self.placeHolderLabel.hidden = YES;
    
    UITouch*  touch = [[event touchesForView:self] anyObject];
    self.previousLocation = [touch locationInView:self];
    
    CGPoint location = self.previousLocation;
    location.y = self.bounds.size.height - self.previousLocation.y;
    self.previousLocation = location;
    
    CGPoint currentPoint = [touch locationInView:self];
    NSDictionary *dict = @{@"x":@(currentPoint.x),@"y":@(currentPoint.y)};
    
    [self.pointsArrM addObject:dict];
    
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    
    UITouch* touch = [[event touchesForView:self] anyObject];
    
    self.location = [touch locationInView:self];
    CGPoint glocation =  self.location;
    glocation.y = self.bounds.size.height - self.location.y;
    self.location = glocation;
    
    //
    self.previousLocation = [touch previousLocationInView:self];
    CGPoint plocation = self.previousLocation;
    plocation.y = self.bounds.size.height - self.previousLocation.y;
    self.previousLocation = plocation;
    
    [self renderLineFromPoint:self.previousLocation toPoint:self.location];
    
    CGPoint currentPoint = [touch locationInView:self];
    NSDictionary *dict = @{@"x":@(currentPoint.x),@"y":@(currentPoint.y)};
    [self.pointsArrM addObject:dict];
 
    
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    
    UITouch*  touch = [[event touchesForView:self] anyObject];
    self.location = [touch locationInView:self];
    
    CGPoint glocation =  self.location;
    glocation.y = self.bounds.size.height - self.location.y;
    self.location = glocation;
    
    self.previousLocation = [touch previousLocationInView:self];
    
    _previousLocation.y = self.bounds.size.height - _previousLocation.y;
    
    [self renderLineFromPoint:self.previousLocation toPoint:self.location];
    
    self.location = CGPointMake(0, 0);
    self.previousLocation = CGPointMake(0, 0);
    
    CGPoint currentPoint = [touch locationInView:self];
    NSDictionary *dict = @{@"x":@(currentPoint.x),@"y":@(currentPoint.y)};
    [self.pointsArrM addObject:dict];
    
}



// 根据两点画线的方法
- (void)renderLineFromPoint:(CGPoint)start toPoint:(CGPoint)end
{
    //     NSLog(@"drawLineWithPoints--%@--%@",NSStringFromCGPoint(start),NSStringFromCGPoint(end));
    static GLfloat*        vertexBuffer = NULL;
    static NSUInteger    vertexMax = 64;
    NSUInteger            vertexCount = 0,
    count,
    i;
    
    [EAGLContext setCurrentContext:self.context];
    glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer);
    
    // Convert locations from Points to Pixels
    CGFloat scale = self.contentScaleFactor;
    
    start.x *= scale;
    start.y *= scale;
    end.x *= scale;
    end.y *= scale;
    
    // Allocate vertex array buffer
    if(vertexBuffer == NULL)
        vertexBuffer = malloc(vertexMax * 2 * sizeof(GLfloat));
    
    // Add points to the buffer so there are drawing points every X pixels
    count = MAX(ceilf(sqrtf((end.x - start.x) * (end.x - start.x) + (end.y - start.y) * (end.y - start.y)) / kBrushPixelStep), 1);
    for(i = 0; i < count; ++i) {
        if(vertexCount == vertexMax) {
            vertexMax = 2 * vertexMax;
            vertexBuffer = realloc(vertexBuffer, vertexMax * 2 * sizeof(GLfloat));
        }
        
        vertexBuffer[2 * vertexCount + 0] = start.x + (end.x - start.x) * ((GLfloat)i / (GLfloat)count);
        vertexBuffer[2 * vertexCount + 1] = start.y + (end.y - start.y) * ((GLfloat)i / (GLfloat)count);
        vertexCount += 1;
    }
    
    // Load data to the Vertex Buffer Object
    glBindBuffer(GL_ARRAY_BUFFER, vboId);
    glBufferData(GL_ARRAY_BUFFER, vertexCount*2*sizeof(GLfloat), vertexBuffer, GL_STATIC_DRAW);
    
    
    glEnableVertexAttribArray(ATTRIB_VERTEX);
    glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, GL_FALSE, 0, 0);
    
    // Draw
    glUseProgram(program[PROGRAM_POINT].id);
    
    // 画线
    glDrawArrays(GL_POINTS, 0, (int)vertexCount);
    
    // Display the buffer
    glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
    [self.context presentRenderbuffer:GL_RENDERBUFFER];
}


#pragma mark - 初始化
- (BOOL)setupInitGL
{
   //1.创建帧缓冲管线
    glGenFramebuffers(1, &viewFramebuffer);
    
    //2.创建渲染缓冲管线
    glGenRenderbuffers(1, &viewRenderbuffer);
    
    //3.绑定帧缓冲管线
    glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer);
    
    //4.绑定渲染缓冲g线
    glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
    
    //5.将渲染缓冲区附加到帧缓冲区上
    [self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(id<EAGLDrawable>)self.layer];
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, viewRenderbuffer);
    
    //6.设定画布的大小
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
    
    //7.初始化
    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
        return NO;
    }
    
    //8.创建显示区域
    glViewport(0, 0, backingWidth, backingHeight);
    
    //9.创建缓存对象
    glGenBuffers(1, &vboId);
    
    
    //10.设置笔纹理
     brushTexture = [self createTextureFromName:@"Particle.png"];
    
    //初始化着色器
    [self setupInitShaders];
    
    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    
    return YES;
}
//创建一个纹理的图像
- (ZBTextureInfoStruct)createTextureFromName:(NSString *)name
{
    ZBTextureInfoStruct   texture;
    
    //1.获取图片
    UIImage *image = [UIImage imageNamed:name];
    
   CGImageRef brushImage = image.CGImage;
    
    //2.获取图片的宽、高
   size_t imageWidth = CGImageGetWidth(brushImage);
   size_t imageHeight = CGImageGetHeight(brushImage);
    
   // 3.判断图片
    {
        //3.1.分配位图上下文所需的内存
        GLubyte  *brushData = (GLubyte *) calloc(imageWidth * imageHeight * 4, sizeof(GLubyte));
        
        //3.2创建位图上下文
        CGContextRef brushContext = CGBitmapContextCreate(brushData, imageWidth, imageHeight, 8, imageWidth * 4, CGImageGetColorSpace(brushImage), kCGImageAlphaPremultipliedLast);
        
        //3.3描绘image
        CGContextDrawImage(brushContext, CGRectMake(0.0, 0.0, (CGFloat)imageWidth, (CGFloat)imageHeight), brushImage);
        
        //3.4 释放上下文
        CGContextRelease(brushContext);
        
        //3.5创建渲染缓冲管线
        GLuint          texId;
        glGenTextures(1, &texId);
        
        //3.6绑定渲染缓冲管线
        glBindTexture(GL_TEXTURE_2D, texId);
        
        //3.7 使用一个缩小过滤器和一个线性过滤器(设置纹理参数加权平均)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        
        // 3.8 2D绘制
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)imageWidth, (int)imageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, brushData);
        
        //3.9 释放数据
        free(brushData);
        
        texture.id = texId;
        texture.width = (int)imageWidth;
        texture.height = (int)imageHeight;
    }
    return texture;
}

//初始化着色器
- (void)setupInitShaders
{
    for (NSInteger i = 0; i < NUM_PROGRAMS; i++) {
       char *vsrc = zb_ReadFile(zb_pathForResource(program[i].vert));
       char *fsrc = zb_ReadFile(zb_pathForResource(program[i].frag));
        GLsizei attribCt = 0;
        GLchar *attribUsed[NUM_ATTRIBS];
        GLint attrib[NUM_ATTRIBS];
        GLchar *attribName[NUM_ATTRIBS] = {
            "inVertex",
        };
        const GLchar *uniformName[NUM_UNIFORMS] = {
            "MVP", "pointSize", "vertexColor", "texture",
        };
        for (int j = 0; j < NUM_ATTRIBS; j++)
        {
            if (strstr(vsrc, attribName[j]))
            {
                attrib[attribCt] = j;
                attribUsed[attribCt++] = attribName[j];
            }
        }
        
        glueCreateProgram(vsrc, fsrc,
                          attribCt, (const GLchar **)&attribUsed[0], attrib,
                          NUM_UNIFORMS, &uniformName[0], program[i].uniform,
                          &program[i].id);
        free(vsrc);
        free(fsrc);
        
        // Set constant/initalize uniforms
        if (i == PROGRAM_POINT)
        {
            glUseProgram(program[PROGRAM_POINT].id);
            
            // the brush texture will be bound to texture unit 0
            glUniform1i(program[PROGRAM_POINT].uniform[UNIFORM_TEXTURE], 0);
            
            // viewing matrices
            GLKMatrix4 projectionMatrix = GLKMatrix4MakeOrtho(0, backingWidth, 0, backingHeight, -1, 1);
            GLKMatrix4 modelViewMatrix = GLKMatrix4Identity; // this sample uses a constant identity modelView matrix
            GLKMatrix4 MVPMatrix = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix);
            
            glUniformMatrix4fv(program[PROGRAM_POINT].uniform[UNIFORM_MVP], 1, GL_FALSE, MVPMatrix.m);
            
            //设置画笔的宽度
            if(self.lineWidth <= 0){
               glUniform1f(program[PROGRAM_POINT].uniform[UNIFORM_POINT_SIZE], brushTexture.width / kBrushScale);
            }else{
              glUniform1f(program[PROGRAM_POINT].uniform[UNIFORM_POINT_SIZE], brushTexture.width* self.lineWidth);
            }
            
            
            // initialize brush color
            glUniform4fv(program[PROGRAM_POINT].uniform[UNIFORM_VERTEX_COLOR], 1, brushColor);
        }
    }
}

- (BOOL)resizeFromLayer:(CAEAGLLayer *)layer
{
    // Allocate color buffer backing based on the current layer size
    glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
    [self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer];
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
    
   
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        NSLog(@"Failed to make complete framebuffer objectz %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
        return NO;
    }
    
    // Update projection matrix
    GLKMatrix4 projectionMatrix = GLKMatrix4MakeOrtho(0, backingWidth, 0, backingHeight, -1, 1);
    GLKMatrix4 modelViewMatrix = GLKMatrix4Identity; // this sample uses a constant identity modelView matrix
    GLKMatrix4 MVPMatrix = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix);
    
    glUseProgram(program[PROGRAM_POINT].id);
    glUniformMatrix4fv(program[PROGRAM_POINT].uniform[UNIFORM_MVP], 1, GL_FALSE, MVPMatrix.m);
    
    // Update viewport
    glViewport(0, 0, backingWidth, backingHeight);
    
    return YES;
}



#pragma mark - private
- (NSArray *)getRGBWithColor:(UIColor *)color
{
    CGFloat red = 0.0;
    CGFloat green = 0.0;
    CGFloat blue = 0.0;
    CGFloat alpha = 0.0;
    [color getRed:&red green:&green blue:&blue alpha:&alpha];
    return @[@(red), @(green), @(blue), @(alpha)];
}

// 保存当前一笔的所有点
- (NSMutableArray *)pointsArrM{
    if (!_pointsArrM) {
        _pointsArrM = [NSMutableArray array];
    }
    return _pointsArrM;
}
- (void)dealloc
{
    //销毁
    if (viewFramebuffer) {
        glDeleteFramebuffers(1, &viewFramebuffer);
        viewFramebuffer = 0;
    }
    if (viewRenderbuffer) {
        glDeleteRenderbuffers(1, &viewRenderbuffer);
        viewRenderbuffer = 0;
    }
    // texture
    if (brushTexture.id) {
        glDeleteTextures(1, &brushTexture.id);
        brushTexture.id = 0;
    }
    // vbo
    if (vboId) {
        glDeleteBuffers(1, &vboId);
        vboId = 0;
    }
    
    // tear down context
    if ([EAGLContext currentContext] == self.context)
        [EAGLContext setCurrentContext:nil];
}

- (UILabel *)placeHolderLabel
{
    if(_placeHolderLabel == nil){
        _placeHolderLabel = [UILabel new];
        _placeHolderLabel.textColor = [UIColor grayColor];
        _placeHolderLabel.textAlignment = NSTextAlignmentCenter;
        _placeHolderLabel.text = @"请在空白地方签名";
    }
    return _placeHolderLabel;
}
@end
